package com.lagou.edu.message.server;

import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.corundumstudio.socketio.*;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;

import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.ExceptionListener;
import com.corundumstudio.socketio.namespace.Namespace;
import com.corundumstudio.socketio.protocol.Packet;
import com.corundumstudio.socketio.protocol.PacketType;
import com.corundumstudio.socketio.store.pubsub.DispatchMessage;
import com.lagou.edu.common.jwt.JwtUtil.JwtResult;
import com.lagou.edu.common.string.GsonUtil;
import com.lagou.edu.message.api.dto.Message;
import com.lagou.edu.message.consts.Constants;
import com.lagou.edu.message.server.dicpacth.Dispatcher;
import com.lagou.edu.message.server.store.StoreFacotryProvider;
import com.lagou.edu.message.util.PushUtils;
import com.lagou.edu.message.util.ServerConfigUtils;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PushServer {

    public static final PushServer pushServer = new PushServer();
    private Namespace pushNamespace;
    private SocketIOServer server;
    private List<SocketIOClient> allClients = new ArrayList<>();

    private PushServer() {
        final Configuration configuration = new Configuration();
        configuration.setStoreFactory(StoreFacotryProvider.getRedissonStoreFactory());
        configuration.setPort(ServerConfigUtils.instance.getWebSocketPort());
        configuration.setContext(ServerConfigUtils.instance.getWebSocketContext());
        configuration.setOrigin(ServerConfigUtils.instance.getWebSocketOrigin());

        // 可重用地址， 防止处于TIME_WAIT的socket影响服务启动
        final SocketConfig socketConfig = new SocketConfig();
        socketConfig.setReuseAddress(true);
        configuration.setSocketConfig(socketConfig);

        server = new SocketIOServer(configuration);
        pushNamespace = (Namespace) server.addNamespace(ServerConfigUtils.instance.getWebSocketContext());

        configuration.setExceptionListener(new ExceptionListener() {
            @Override
            public void onEventException(Exception e, List<Object> list, SocketIOClient socketIOClient) {
                UUID sessionId = socketIOClient.getSessionId();
                log.error("onEventException, sessionId:{}, roomList:{}", sessionId, list);
            }

            @Override
            public void onDisconnectException(Exception e, SocketIOClient socketIOClient) {
                log.error("onDisconnectException, sessionId:{}", socketIOClient.getSessionId());
            }

            @Override
            public void onConnectException(Exception e, SocketIOClient socketIOClient) {
                log.error("onConnectException, sessionId:{}", socketIOClient.getSessionId());
            }

            @Override
            public void onPingException(Exception e, SocketIOClient socketIOClient) {
                log.error("onPingException, sessionId:{}", socketIOClient.getSessionId());
            }

            @Override
            public boolean exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable) throws Exception {
                log.error("exceptionCaught");
                return false;
            }
        });

        pushNamespace.addConnectListener(new ConnectListener() {
            @Override
            public void onConnect(SocketIOClient client) {
                // 获取的数据
                UUID sessionId = client.getSessionId();
                JwtResult userInfo = UserAuthorizationListener.getUserInfo(client.getHandshakeData());
                if (userInfo != null) {
                    allClients.add(client);
//                    Set<String> allRooms = client.getAllRooms();
//                    for (String allRoom : allRooms) {
//                        client.leaveRoom(allRoom);
//                    }
//
//                    String userId = Integer.toString(userInfo.getUserId());
//                    List<String> roomList = PushUtils.getRoomList(userId, null, null);
//                    for (String roomStr : roomList) {
//                        client.joinRoom(roomStr);
//                        log.info("userId:{} sid:{} join room {}", userId, sessionId, roomStr);
//                    }
//                    client.set(Constants.ROOM, roomList);
                } else {
                    client.disconnect();
                }
            }
        });

        pushNamespace.addDisconnectListener(new DisconnectListener() {
            @Override
            public void onDisconnect(SocketIOClient client) {
                UUID sessionId = client.getSessionId();
                JwtResult userInfo = UserAuthorizationListener.getUserInfo(client.getHandshakeData());

                if (userInfo != null) {
                    log.info("disconnect userid:{}, userName:{}, sid:{}", userInfo.getUserId(), userInfo.getUserName(), sessionId);
                } else {
                    client.disconnect();
                }

                allClients.remove(client);
//
//                String userId = Integer.toString(userInfo.getUserId());
//                List<String> roomList = PushUtils.getRoomList(userId, null, null);
//                for (String roomStr : roomList) {
//                    client.joinRoom(roomStr);
//                    log.info("userId:{} sid:{} join room {}", userId, sessionId, roomStr);
//                }
//
//                try {
//                    List<String> oldRoomList = client.get(Constants.ROOM);
//                    if (null != oldRoomList && oldRoomList.size() > 0) {
//                        for (String room : oldRoomList) {
//                            if (StringUtils.isBlank(room)) {
//                                continue;
//                            }
//                            client.leaveRoom(room);
//                        }
//                    }
//                    client.disconnect();
//                } catch (Exception e) {
//                    e.printStackTrace();
//                } finally {
//                    try {
//                        client.del(Constants.ROOM);
//                    }catch (Exception e) {
//                        e.printStackTrace();
//                    }
//
//                }
            }
        });
    }

    /**
     * 推送消息
     *
     * @param message
     */
    public void push(Message message) {
    	String room;

        // 组装消息
        Packet packet = new Packet(PacketType.MESSAGE);
        packet.setSubType(PacketType.EVENT);
        packet.setName("message");
        List<Object> data = new ArrayList<>();
        data.add(GsonUtil.toJson(message));
        packet.setData(data);
        packet.setNsp(pushNamespace.getName());

        try {
            for (SocketIOClient client : allClients) {
                client.send(packet);
            }

//    	    if (message.getUserId() == null) {
//    	        throw new NullPointerException("userId不能为空");
//            }
//
//            room = PushUtils.getRoom(null, message.getUserId(), null);
//
//    	    // 组装消息
//            Packet packet = new Packet(PacketType.MESSAGE);
//            packet.setSubType(PacketType.EVENT);
//            packet.setName("message");
//            List<Object> data = new ArrayList<>();
//            data.add(GsonUtil.toJson(message));
//            packet.setData(data);
//            packet.setNsp(pushNamespace.getName());
//
//            // 推送
//            Iterable<SocketIOClient> clients = pushNamespace.getRoomClients(room);
//            for (SocketIOClient client : clients) {
//                client.send(packet);
//            }
        } finally {

        }
    }

    /**
     * 同步启动服务；
     */
    public void start() {
        try {
            server.start();
        } catch (Exception e) {
            log.error("Push server start failed!", e);
            System.exit(-1);
        }
    }

    /**
     * 停止服务
     */
    public void stop() {
        server.stop();
    }

    public Map<String, Object> getStatus() {
        HashMap<String, Object> status = new HashMap<>();
        status.put("namespace", pushNamespace.getName());   // namespace
        status.put("rooms", pushNamespace.getRooms());
        status.put("clients", pushNamespace.getAllClients().size());
        return status;
    }

}
